/******************************************************************************
 *                       B O M B E R M A N  2 0 1 2
 * 
 * File Name: Bomberman.java
 * 
 * COP 4331 Summer 2012 Project
 * 
 * Authors: Trevor Mancuso, Chris Chiarella, Zach Hoyler, Daniel Martin
 * 
 * 
 * Description: This is the main class. It keeps track of bombers/AI as well as
 * repaints the canvas every frame. It also sets up the controls for players.
 * 
 ******************************************************************************/
package bomberman;

import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Bomberman implements KeyListener {
    
    // Time limit of a game in seconds
    public final int TIME_LIMIT = 120;   
    
    // The width of the play area (in pixels)
    private final int PLAY_WIDTH = 600; 
    
    // The height of the play area (in pixels)
    private final int PLAY_HEIGHT = 633; 

    // Number of squares (height and width) of a level
    private static int levelSize = 30;

    // Number of seconds left in the current game
    private int timeLeft;
    
    // Time since last frame update in milliseconds
    private long delta;
        
    private boolean gameOver = false;
    
    // Sudden death mode (enabled when time runs out)
    private boolean suddenDeath = false;

    // The window itself
    private JFrame frame;
    
    // The area where the game takes place
    private JPanel playArea;
    
     // Size of the squares rendered
    private int squareSize = 20;

    public Level theGame = new Level(levelSize);

    public Bomber[] bombers; 
    public int numPlayers = 2;
    public int ai_ID = -1;

    public String myAddress;
    public String opAddress;
    public GameSocket socket;
    public boolean host;
    public boolean network = false;
    public NetworkHandler handler;
    public Thread socketThread;
    public boolean ready = false;
    public boolean connected = false;
    //Enum to load all the images at the start of the game.
    public static enum Sprites {
        BACKGROUND("background.png"),
        BARREL("barrel.png"),
        BARRIER("barrier.png"),
        BARRIER_IND("barrier_ind.png"),
        BOMB("bomb.png"),
        CHARACTER_BLUE_DEAD("character_blue_dead.png"),
        CHARACTER_BLUE_DOWN("character_blue_down.png"),
        CHARACTER_BLUE_LEFT("character_blue_left.png"),
        CHARACTER_BLUE_RIGHT("character_blue_right.png"),
        CHARACTER_BLUE_UP("character_blue_up.png"),
        CHARACTER_GREEN_DEAD("character_green_dead.png"),
        CHARACTER_GREEN_DOWN("character_green_down.png"),
        CHARACTER_GREEN_LEFT("character_green_left.png"),
        CHARACTER_GREEN_RIGHT("character_green_right.png"),
        CHARACTER_GREEN_UP("character_green_up.png"),
        CHARACTER_RED_DEAD("character_red_dead.png"),
        CHARACTER_RED_DOWN("character_red_down.png"),
        CHARACTER_RED_LEFT("character_red_left.png"),
        CHARACTER_RED_RIGHT("character_red_right.png"),
        CHARACTER_RED_UP("character_red_up.png"),
        CRATE("crate.png"),
        EXPLOSION("explosion.png"),
        BOMB_COUNT("bomb_count.png"),
        BOMB_POWER("bomb_power.png"),
        REMOTE("remote.png"),
        VIRUS("virus.png");

        private Image sprite;

        Sprites(String fileName)
        {
            sprite = Toolkit.getDefaultToolkit().getImage(getClass().getResource
                    ("/images/" + fileName));
        }

        public Image getSprite() 
        {
            return sprite;
        }
    }
    //Constructor for local games.    
    public Bomberman(int numPlayers, int ai_ID)
    {
        bombers = new Bomber[numPlayers];
        this.ai_ID = ai_ID;
        this.numPlayers = numPlayers;
        configureGUI();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        
        frame.addWindowListener( 
                new WindowAdapter()
                {
                    @Override
                    public void windowClosing(WindowEvent e)
                    {
                        System.out.println("CLOSE TEST!");
                    }
                });
        
        frame.setVisible(true);
        play();
    }
    //Constructor for network games.
    public Bomberman(String opAddress, boolean host)
    {
        bombers = new Bomber[numPlayers];
        this.opAddress = opAddress;
        this.host = host;
        network = true;
        handler = new NetworkHandler(this, opAddress);
        
        configureGUI();        
        frame.setVisible(true);
        
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                
                frame.addWindowListener( 
                new WindowAdapter()
                {

            @Override //Close the sockets upon closing the window.
                    public void windowClosing(WindowEvent e)
                    {
                        System.out.println("TESTING WINDOW CLOSE 1");
                        socket.closeSockets();
                        System.out.println("TESTING WINDOW CLOSE 2");
                        socket.stopRunning();
                    }
                });
        //Networking setup for the host
        if (host)
        {
            try
            {
                //create a new socket
                socket = new ServerSocket(this);
                
                socketThread = new Thread(socket);
                socketThread.start();
                
                Graphics g = playArea.getGraphics();
                Image bufferImage = frame.createImage(PLAY_WIDTH, PLAY_HEIGHT);
                Graphics T = bufferImage.getGraphics();  
                //wait for the client to connect
                while (!ready)
                {
                    T.setColor(Color.RED);
                    Font f = new Font("Dialog" , Font.BOLD, 40);
                    T.setFont(f); 
                    T.drawString("Waiting for a client...", 50, 225);
                    g.drawImage(bufferImage, 0, 0, frame);
                }
                
                System.out.println("Client IP: " + opAddress);
                
                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                
            } catch (IOException ex)
            {
                Logger.getLogger(Bomberman.class.getName()).log(
                        java.util.logging.Level.SEVERE, null, ex);
            }
        }
        //Setup for client
        else
        {
            try
            {
                //create a new socket
                socket = new ClientSocket(this);
                
                socketThread = new Thread(socket);
                socketThread.start();
                
                long curTime = System.currentTimeMillis();
                
                handler.requestConnection();
                handler.requestLevel();
                
                Graphics g = playArea.getGraphics();
                Image bufferImage = frame.createImage(PLAY_WIDTH, PLAY_HEIGHT);
                Graphics T = bufferImage.getGraphics(); 
                //request the level from the server until received
                while (!ready)
                {
                    long time = System.currentTimeMillis();
                    
                    T.setColor(Color.RED);
                    Font f = new Font("Dialog",Font.BOLD, 40);
                    T.setFont(f); 
                    T.drawString("Waiting for the server...", 15, 225);
                    g.drawImage(bufferImage, 0, 0, frame);
                    
                    if (time - curTime >= 2000)
                    {
                        System.out.println("Requesting level");
                        handler.requestConnection();
                        handler.requestLevel();
                        curTime = System.currentTimeMillis();
                        
                        T.setColor(Color.RED);
                        f = new Font("Dialog", Font.BOLD, 40);
                        T.setFont(f); 
                        T.drawString("Waiting for the server...", 15, 225);
                        g.drawImage(bufferImage, 0, 0, frame);
                        
                    }
                    
                }
                
                
            } catch (SocketException ex)
            {
                Logger.getLogger(Bomberman.class.getName()).log(
                        java.util.logging.Level.SEVERE, null, ex);
            } catch (UnknownHostException ex)
            {
                Logger.getLogger(Bomberman.class.getName()).log(
                        java.util.logging.Level.SEVERE, null, ex);
            }
        }
        
        play();
    }
    //return the size of the level
    public static int getLevelSize()
    {
        return levelSize;
    }
    //initialize the players and start the game
    private void play()
    {
        for (int i = 0; i < numPlayers; i++)
        {
            // Set second player to be AI (temporary)
            if (i == ai_ID)
                bombers[i] = new Bomber(i + 1, this, true);
            else
                bombers[i] = new Bomber(i + 1, this, false);
        }
        
        gameOver = false;
        timeLeft = TIME_LIMIT;        
        Timer gameTime = new Timer();
        
        gameTime.schedule(
                new TimerTask()
                {
                    @Override
                    public void run() 
                    {
                        if (timeLeft > 0)
                        {
                            timeLeft--;

                            if (timeLeft == 0)
                                Sound.SUDDENDEATH.play();
                        }
                        else
                        {
                            suddenDeath = true;
                            this.cancel();
                        }

                        if (gameOver)
                            this.cancel();

                        if (timeLeft <= 10 && timeLeft > 0)
                            Sound.COUNTDOWN.play();
                    }
            
                }, 0, 1000);
        
        long lastTime = System.nanoTime();        
        long lastVirusTick = lastTime;
        long lastSuddenDeathTick = lastTime;
        long lastAITick = lastTime;
        
        while (true)
        {
            long currentTime = System.nanoTime();
            delta = (currentTime - lastTime) / 1000000;
            
            if ((currentTime - lastVirusTick) / 1000000 >= Powerup.VIRUS_DELAY)
            {
                for(int i = 0; i < numPlayers; i++)
                {
                    bombers[i].virusTick();
                }
                lastVirusTick = currentTime;
            }
            
            if ((currentTime - lastAITick) / 1000000 >= Bomber.AI_DELAY)
            {
                for (Bomber b : bombers)
                {
                    if (b != null && b.isAI())
                        b.AI(bombers);
                }
                lastAITick = currentTime;
            }
            
            if (!gameOver && suddenDeath &&
                    (currentTime - lastSuddenDeathTick) / 1000000
                        >= theGame.SUDDEN_DEATH_DELAY)
            {
                theGame.suddenDeath(this);
                lastSuddenDeathTick = currentTime;
            }
            
            lastTime = currentTime;
            draw();
        }
    }
    
    // Time since last frame in milliseconds
    public long delta()
    {
        return delta;
    }
    //draw the canvas
    private void draw()
    {
        Graphics g = playArea.getGraphics();

        Image bufferImage = frame.createImage(PLAY_WIDTH, PLAY_HEIGHT);
        Graphics T = bufferImage.getGraphics();        

        T.setColor(Color.black);
        T.fillRect(0, 0, PLAY_WIDTH, PLAY_HEIGHT);
        
        T.setColor(Color.YELLOW);
        Font f = new Font("Dialog",Font.BOLD,13);
        T.setFont(f);
        
        T.drawString(
                "PLAYER 1 - BOMBS:"
                + (bombers[0].maxBombs - bombers[0].numBombs)
                + "  POWER:"
                + bombers[0].bombPower
                + "                       |                      "
                + "PLAYER 2 - BOMBS:"
                + (bombers[1].maxBombs - bombers[1].numBombs)
                + "  POWER:"
                + bombers[1].bombPower , 5, 615);
        
        T.drawString("TIME LEFT:" + timeLeft, PLAY_WIDTH/2 - 50, 628);
        
        // Render background
        for(int y = 0; y < levelSize; y++)
        {
            for(int x = 0; x < levelSize; x++)
            {
                
                
                 T.drawImage(Bomberman.Sprites.BACKGROUND.getSprite(), x*squareSize,
                                y*squareSize, squareSize, squareSize, frame);
            }
            
        }
        
        // Render level
        for(int y = 0; y < levelSize; y++)
        {
            for(int x = 0; x < levelSize; x++)
            {
                Barrier tempBarrier = theGame.getBarrier(x,y);
                if (tempBarrier != null)
                        T.drawImage(tempBarrier.getSprite(), x*squareSize,
                                y*squareSize, squareSize, squareSize, frame);
            }
            
        }
        
        // Render players
        for(int i = 0; i < numPlayers; i++)
        {
            /**
             * Modified to Draw sprite
             * -Chris
            */
            T.drawImage(bombers[i].getSprite(),
                    bombers[i].getX() * squareSize,
                    bombers[i].getY() * squareSize,
                    squareSize, squareSize,
                    frame);
            
            if (!bombers[i].alive)
            {                
                T.setColor(Color.RED);
                
            }
        }
        
        // Render bombs
        for (int y = 0; y < levelSize; y++)
        {
            for (int x = 0; x < levelSize; x++)
            {
                Bomb tempBomb = theGame.getBomb(x, y);
                if (tempBomb != null)
                    /**
                     * Modified to Draw bomb
                     * -Chris
                    */
                    T.drawImage(tempBomb.getSprite(),
                            x*squareSize, y*squareSize,
                            squareSize, squareSize, frame);
            }
        }
        
        // Render explosions
        for (int y = 0; y < levelSize; y++)
        {
            for (int x = 0; x < levelSize; x++)
            {
                Explosion tempExplosion = theGame.getExplosion(x, y);
                if (tempExplosion != null)
                    T.drawImage(tempExplosion.getSprite(),
                            x*squareSize, y*squareSize,
                            squareSize, squareSize, frame);
            }
        }
        
        // Render powerups
        for (int y = 0; y < levelSize; y++)
        {
            for (int x = 0; x < levelSize; x++)
            {
                Powerup tempPowerup = theGame.getPowerup(x, y);
                if (tempPowerup != null)
                    
                    /**
                    * Modified to Draw power-up
                    * -Chris
                    */
                    T.drawImage(tempPowerup.getSprite(),
                            x*squareSize, y*squareSize,
                            squareSize, squareSize, frame);
            }
        }
        //win conditions
        int alivePlayer = 0;
        int aliveCount = 0;
        for (int i = 0; i < numPlayers; i++)
        {
            if (bombers[i].alive)
            {
                aliveCount++;
                alivePlayer = i;
            }            
        }
        if (aliveCount == 1)
        {
            T.setColor(Color.RED);
            f = new Font("Dialog", Font.BOLD, 40);
            T.setFont(f); 
            T.drawString("PLAYER " + (alivePlayer+1) + " WINS!", 130, 325);
            gameOver = true;
        }
        else if (aliveCount == 0)
        {
            T.setColor(Color.RED);
            f = new Font("Dialog", Font.BOLD, 40);
            T.setFont(f); 
            T.drawString("TIE GAME!", 180, 325);
            gameOver = true;
        }
        
        g.drawImage(bufferImage, 0, 0, frame);
    }
    //GUI setup
    private void configureGUI()
    {
        // Create the window object
        frame = new JFrame("Bomberman");
        frame.setSize(PLAY_WIDTH+20, PLAY_HEIGHT+35);
        frame.setResizable(false);

        // The program should end when the window is closed
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // Set the window's layout manager
        frame.setLayout(new FlowLayout());

        // Create the play area
        playArea = new JPanel();
        playArea.setPreferredSize(new Dimension(PLAY_WIDTH, PLAY_HEIGHT));
        playArea.setBackground(Color.BLACK);
        playArea.setFocusable(false);
        frame.add(playArea);

        frame.addKeyListener(this);
    }

    @Override 
    public void keyPressed(KeyEvent key)
    {
        //Keybindings
        if (!network)
        {
            // Mark down which important keys have been pressed
            if (key.getKeyCode() == KeyEvent.VK_UP)
                bombers[0].move(1);
            if (key.getKeyCode() == KeyEvent.VK_LEFT)
                bombers[0].move(4);
            if (key.getKeyCode() == KeyEvent.VK_RIGHT)
                bombers[0].move(2);
            if (key.getKeyCode() == KeyEvent.VK_DOWN)
                bombers[0].move(3);
            if (key.getKeyCode() == KeyEvent.VK_SPACE)
                bombers[0].placeBomb();
            if (key.getKeyCode() == KeyEvent.VK_SHIFT)
                bombers[0].remoteDetonate();

            if (numPlayers > 1 && !bombers[1].isAI())
            {
                if (key.getKeyCode() == KeyEvent.VK_W)
                    bombers[1].move(1);
                if (key.getKeyCode() == KeyEvent.VK_A)
                    bombers[1].move(4);
                if (key.getKeyCode() == KeyEvent.VK_D)
                    bombers[1].move(2);
                if (key.getKeyCode() == KeyEvent.VK_S)
                    bombers[1].move(3);
                if (key.getKeyCode() == KeyEvent.VK_CONTROL)
                    bombers[1].placeBomb();
                if (key.getKeyCode() == KeyEvent.VK_E)
                    bombers[1].remoteDetonate();

            }
        }
        if (network)
        {
            if (host)
            {
                if (key.getKeyCode() == KeyEvent.VK_UP)
                    bombers[0].move(1);
                if (key.getKeyCode() == KeyEvent.VK_LEFT)
                    bombers[0].move(4);
                if (key.getKeyCode() == KeyEvent.VK_RIGHT)
                    bombers[0].move(2);
                if (key.getKeyCode() == KeyEvent.VK_DOWN)
                    bombers[0].move(3);
                if (key.getKeyCode() == KeyEvent.VK_SPACE)
                    bombers[0].placeBomb();
                if (key.getKeyCode() == KeyEvent.VK_SHIFT)
                    bombers[0].remoteDetonate();
            }
            if (!host)
            {
                if (key.getKeyCode() == KeyEvent.VK_UP)
                    bombers[1].move(1);
                if (key.getKeyCode() == KeyEvent.VK_LEFT)
                    bombers[1].move(4);
                if (key.getKeyCode() == KeyEvent.VK_RIGHT)
                    bombers[1].move(2);
                if (key.getKeyCode() == KeyEvent.VK_DOWN)
                    bombers[1].move(3);
                if (key.getKeyCode() == KeyEvent.VK_SPACE)
                    bombers[1].placeBomb();
                if (key.getKeyCode() == KeyEvent.VK_SHIFT)
                    bombers[1].remoteDetonate();
            }
        }
    }

    // Deals with keyboard keys being released
    @Override
    public void keyReleased(KeyEvent key)
    {
    }

    // This method is not actually used, 
    // but is required by the KeyListener interface
    @Override
    public void keyTyped(KeyEvent key)
    {
    }

    
    public static void main(String[] args)
    {
        new Bomberman(2, 1);
    }
}
